local function clone(table)
	local result = {}
	for k, v in pairs(table) do
		result[k] = v
	end
	return result
end

api = {
	global = {
		--[[ re-implemented to hide the simulation
		_G = _G,
		getfenv = getfenv,
		getmetatable = getmetatable,
		rawget = rawget,
		rawset = rawset,
		setfenv = setfenv,
		setmetatable = setmetatable,
		getglobal = wow.getglobal,
		setglobal = wow.setglobal,
		debugstack = wow.debugstack,
		]]

		--[[ not available
		dofile = dofile,
		load = load,
		module = module,
		print = print,
		require = require,
		debug = clone(debug),
		io = clone(io),
		os = clone(os),
		package = clone(package),
		]]

		_VERSION = _VERSION,
		assert = assert,
		-- collectgarbage = collectgarbage,
		gcinfo = gcinfo,
		error = error,
		ipairs = ipairs,
		-- loadstring = loadstring,
		next = next,
		pairs = pairs,
		pcall = pcall,
		rawequal = rawequal,
		select = select,
		tonumber = tonumber,
		tostring = tostring,
		type = type,
		unpack = unpack,
		xpcall = xpcall,
		coroutine = clone(coroutine),
		math = clone(math),
		string = clone(string),
		table = clone(table),

		--[[ added by wow ]]
		bit = clone(bit),
		date = os.date,
		time = os.time,
		difftime = os.difftime,
		abs = math.abs,
		acos = math.acos,
		asin = math.asin,
		atan = math.atan,
		atan2 = math.atan2,
		ceil = math.ceil,
		cos = math.cos,
		deg = math.deg,
		exp = math.exp,
		floor = math.floor,
		frexp = math.frexp,
		ldexp = math.ldexp,
		log = math.log,
		log10 = math.log10,
		max = math.max,
		min = math.min,
		mod = math.fmod,
		rad = math.rad,
		random = math.random,
		randomseed = math.randomseed,
		sin = math.sin,
		sqrt = math.sqrt,
		tan = math.tan,
		format = string.format,
		gmatch = string.gmatch,
		gsub = string.gsub,
		strbyte = string.byte,
		strchar = string.char,
		strfind = string.find,
		strlen = string.len,
		strlower = string.lower,
		strmatch = string.match,
		strrep = string.rep,
		strrev = string.reverse,
		strsub = string.sub,
		strupper = string.upper,
		foreach = table.foreach,
		foreachi = table.foreachi,
		getn = table.getn,
		sort = table.sort,
		tinsert = table.insert,
		tremove = table.remove,
		strtrim = wow.strtrim,
		strsplit = wow.strsplit,
		strjoin = wow.strjoin,
		strconcat = wow.strconcat,
		strreplace = wow.strreplace,
		newproxy = newproxy,
		PI = math.pi,
	},
	current = {},
}

local api = api

setmetatable(api.global.string, {
	__newindex = function (table, key, value)
		if not rawget(string, key) then
			rawset(string, key, value)
		end
		rawset(table, key, value)
	end})

local failedglobals = config.DUMP and {}

local function toliteral(value)
	local t = type(value)
	if t == "string" then
		return string.format("%q", value):gsub("\n", "\\n")
	elseif t == "number" then
		return string.format("%f", value)
	elseif t == "boolean" then
		return value and "true" or "false"
	elseif t == "nil" then
		return "nil"
	elseif t == "table" and type(value[0]) == "userdata" then
		return "frame"
	end
	return "..."
end

function api.dumpfailedglobals()
	if failedglobals then
		local f = io.open("failedglobals.txt", "w")
		for name in pairs(failedglobals) do
			f:write(string.format("%q\n", name))
		end
		f:close()
		f = io.open("globals.txt", "w")
		for name, value in pairs(api.current) do
			f:write(string.format("%s\t%s\t%s\n", name, type(value), toliteral(value)))
		end
		f:close()
	end
end

local function validate_global_name(name)
        if type(name) ~= "string" then
                return true
        end
        if name:len() < 1 then
                return true
        end
        local first = name:sub(1, 1)
        if not first:find("[A-Z_]") then
                return true
        end
        return false
end

local report_globals = setmetatable({
        isViewable = true,
        _ = true,
}, {
        __index = function (self, key)
                local result = validate_global_name(key)
                self[key] = result
                return result
        end,
})

local function env_index(table, key, noreport)
	if not key then return nil end
	local v = api.current[key] or api.global[key]
	if v then
		Security:setaccess(v)
	elseif type(api.env_metatable) == "table" then
		local index = api.env_metatable.__index
		local t = type(index)
		if t == "function" then
			v = index(table, key)
		elseif t == "table" then
			v = index[key]
		end
	end
	if not v and (type(key) ~= "string" or not key:match("^[a-zA-Z_][a-zA-Z0-9_]*$")) then
		-- log (invalid global name)
		return nil
	end
	if not v and not noreport then
		if failedglobals then
			failedglobals[key] = true
		end
		if report_globals[key] then
			local info = debug.getinfo(2)
			if info.source == "=(tail call)" then
				info = debug.getinfo(3)
			end
			log("EE suspicious global read \"%s\" here: %s:%d", key, info.source or "* unknown *", info.currentline or -1)
		end
		return nil
	end
	return v
end

local function env_newindex(table, key, value)
	if value then
		Security:setobject(value)
	end
	local old = api.current[key] or api.global[key]
	if not old and type(api.env_metatable) == "table" then
		local newindex = api.env_metatable.__newindex
		if type(newindex) == "function" then
			return newindex(table, key, value)
		end
	end
	if api.global[key] then
		log("WW base API global \"%s\" overwritten here: %s:%d", key, info.source or "* unknown *", info.currentline or -1)
	end
	if report_globals[key] then
		local info = debug.getinfo(2)
		if old then
			log("EE suspicious global overwritten \"%s\" here: %s:%d", key, info.source or "* unknown *", info.currentline or -1)
		else
			log("WW suspicious global set \"%s\" here: %s:%d", key, info.source or "* unknown *", info.currentline or -1)
		end
	end
	api.current[key] = value
end


local env = setmetatable({}, {
	__index = env_index,
	__newindex = env_newindex,
})

api.global._G = env

function api.global.loadstring(...)
	local result, error = loadstring(...)
	if result then
		setfenv(result, env)
	end
	return result, error
end

function api.global.getfenv(param)
	local e, r = getfenv(0), getfenv(param)
	if r == e then
		r = env
	end
	return r
end

function api.global.getmetatable(table)
	if table == env then
		return api.env_metatable
	else
		return getmetatable(table)
	end
end

function api.global.rawget(table, key)
	if table == env then
		return env_index(table, key)
	else
		return rawget(table, key)
	end
end

function api.global.rawset(table, key, value)
	if table == env then
		return env_newindex(table, key, value)
	else
		return rawset(table, key, value)
	end
end

function api.global.setfenv(f, env)
	if f == 0 then
		log("EE bad bad script is trying to change the thread fenv() :%s", debug.traceback())
	else
		return setfenv(f, env)
	end
end

function api.global.setmetatable(table, mt)
	if table == env then
		api.env_metatable = mt
		return table
	else
		return setmetatable(table, mt)
	end
end

function api.global.getglobal(name)
	return env_index(env, tostring(name), true)
end

function api.global.setglobal(name, value)
	return env_newindex(env, name, value)
end

dofile("debugstack.lua")

function api.global.collectgarbage(...)
	if ... == "count" then
		print("COLLECTGARBAGE", ...)
		return collectgarbage(...)
	else
		local s = collectgarbage("count")
		local res = collectgarbage(...)
		local e = collectgarbage("count")
		print("COLLECTGARBAGE", s, e, ...)
		return res
	end
end

function api.global.RunScript(s)
	local func = assert(loadstring(s))
	setfenv(func, env)
	func()
end
